package com.c6906.vdam.magent.service;

import com.c6906.vdam.magent.model.DiskUsage;
import com.c6906.vdam.magent.model.ServerData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import oshi.SystemInfo;
import oshi.hardware.CentralProcessor;
import oshi.hardware.GlobalMemory;
import oshi.hardware.HardwareAbstractionLayer;
import oshi.software.os.OSFileStore;
import oshi.software.os.OperatingSystem;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;


@Slf4j
@Component
public class MonitoringService {

    @Value("${monitoring.url}")
    private String url;

    @Value("${monitoring.hostname}")
    private String hostname;

    private static long serialId = 0;

    private final SystemInfo systemInfo = new SystemInfo();
    private final HardwareAbstractionLayer hal = systemInfo.getHardware();
    private final OperatingSystem os = systemInfo.getOperatingSystem();
    private long[] prevTicks = new long[CentralProcessor.TickType.values().length];
    private long[][] prevProcTicks = new long[hal.getProcessor().getLogicalProcessorCount()][CentralProcessor.TickType.values().length];

    @Scheduled(fixedRateString = "${monitoring.interval}")
    public void startMonitoring() {
        log.info("Scheduled task started...");
        try {
            log.info("start Monitoring......");
            ServerData serverData = new ServerData();
            CentralProcessor processor = hal.getProcessor();
            long[] ticks = processor.getSystemCpuLoadTicks();
            double totalCpuLoad = processor.getSystemCpuLoadBetweenTicks(prevTicks) * 100;
            prevTicks = ticks;

            double[] coreUsagesArray = processor.getProcessorCpuLoadBetweenTicks(prevProcTicks);
            List<Double> coreUsages = new ArrayList<>();
            for (double load : coreUsagesArray) {
                coreUsages.add(load * 100);
            }
            prevProcTicks = processor.getProcessorCpuLoadTicks();

            GlobalMemory memory = hal.getMemory();
            long totalMemory = memory.getTotal();
            long availableMemory = memory.getAvailable();
            long usedMemory = totalMemory - availableMemory;
            double memoryUsage = (double) usedMemory / totalMemory * 100;

            List<DiskUsage> diskUsages = new ArrayList<>();
            for (OSFileStore fs : os.getFileSystem().getFileStores()) {
                long totalSpace = fs.getTotalSpace();
                long usableSpace = fs.getUsableSpace();
                long usedSpace = totalSpace - usableSpace;
                double percentUsed = (double) usedSpace / totalSpace * 100;

                DiskUsage diskUsage = new DiskUsage(
                        fs.getMount(),
                        serverData.getMsgSerial(),
                        bytesToGigabytes(totalSpace),
                        bytesToGigabytes(usedSpace),
                        percentUsed
                );
                diskUsages.add(diskUsage);
            }

            long eventTime = System.currentTimeMillis();
            serverData.setHostName(hostname);
            serverData.setEventTime(eventTime);
            serverData.setTotalCPUUsage(totalCpuLoad);
            serverData.setMemTotal(totalMemory);
            serverData.setMemAvailable(availableMemory);
            serverData.setMemUsed(usedMemory);
            serverData.setMemAvailable(availableMemory);
            serverData.setMemUsedPercent(memoryUsage);
            serverData.setCoreUsages(coreUsages);
            serverData.setDiskUsages(diskUsages);
            sendServerData(url, serverData);
            log.info("end Monitoring......");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private double bytesToGigabytes(long bytes) {
        return bytes / (1024.0 * 1024.0 * 1024.0);
    }

    private void sendServerData(String urlString, ServerData data) throws Exception {
        log.info("try to connect to {}",urlString);
        URL url = new URL(urlString);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Content-Type", "application/json; utf-8");
        conn.setDoOutput(true);

        String jsonInputString = data.toJson();
        try (OutputStream os = conn.getOutputStream()) {
            byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
            os.write(input);
        }

        try (BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder response = new StringBuilder();
            String responseLine;
            while ((responseLine = br.readLine()) != null) {
                response.append(responseLine.trim());
            }
            System.out.println(response.toString());
        }
    }
}
